package com.wutong.resonance.model.audio.header;

import com.wutong.resonance.model.exception.PluginException;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteOrder;

/**
 * Wave文件头部信息
 * <p>
 * WARN: 需要强制使用小端模式，大端模式会导致读取失败
 *
 * @author wutong
 */
@Slf4j
public class WaveHeader extends FileHeader {
    // "RIFF"
    private static final int CHUNK_ID = 0x52494646;
    // "WAVE"
    private static final int FORMAT = 0x57415645;
    // "fmt "
    private static final int SUBCHUNK1_ID = 0x666d7420;
    // "data"
    private static final int SUBCHUNK2_ID = 0x64617461;
    // fmt 子块的大小 (通常为 16)
    private static final int SUBCHUNK1_SIZE = 16;
    // PCM = 1 (即，线性量化)
    private static final short AUDIO_FORMAT = 1;

    /**
     * 构造函数
     *
     * @param numChannels     声道数
     * @param sampleRate      采样率
     * @param bitsPerSample   采样位数
     * @param subchunk2Size   数据块大小
     */
    public WaveHeader(short numChannels, int sampleRate, short bitsPerSample, int subchunk2Size) {
        super(
                CHUNK_ID, 36 + subchunk2Size, FORMAT,
                SUBCHUNK1_ID, SUBCHUNK1_SIZE, AUDIO_FORMAT,
                numChannels, sampleRate,
                sampleRate * numChannels * (bitsPerSample / 8),
                (short) (numChannels * (bitsPerSample / 8)),
                bitsPerSample, SUBCHUNK2_ID, subchunk2Size,
                ByteOrder.LITTLE_ENDIAN
        );
    }

    @Override
    public byte[] getHeader() {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            writeChar(bos, new char[]{'R', 'I', 'F', 'F'});
            writeInt(bos, getChunkSize());
            writeChar(bos, new char[]{'W', 'A', 'V', 'E'});
            writeChar(bos, new char[]{'f', 'm', 't', ' '});
            writeInt(bos, getSubchunk1Size());
            writeShort(bos, getAudioFormat());
            writeShort(bos, getNumChannels());
            writeInt(bos, getSampleRate());
            writeInt(bos, getByteRate());
            writeShort(bos, getBlockAlign());
            writeShort(bos, getBitsPerSample());
            writeChar(bos, new char[]{'d', 'a', 't', 'a'});
            writeInt(bos, getSubchunk2Size());

            return bos.toByteArray();
        } catch (IOException e) {
            log.error("写入文件头部信息失败: {}", e.getMessage(), e);
            throw new PluginException.PluginCannotLoadException("写入文件头部信息失败: " + e.getMessage(), e);
        }
    }

    @Override
    protected void writeShort(ByteArrayOutputStream bos, int s) throws IOException {
        bos.write(s & 0xFF);
        bos.write((s >> 8) & 0xFF);
    }

    @Override
    protected void writeInt(ByteArrayOutputStream bos, int n) throws IOException {
        bos.write(n & 0xFF);
        bos.write((n >> 8) & 0xFF);
        bos.write((n >> 16) & 0xFF);
        bos.write((n >> 24) & 0xFF);
    }

    @Override
    protected void writeChar(ByteArrayOutputStream bos, char[] id) {
        for (char c : id) {
            bos.write((byte) c);
        }
    }
}